//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <engine.h>

STATIC CGrafixLayer* g_basePlaneLayer = NULL;

CGrafixLayer::~CGrafixLayer()
{
    g_lock.Lock();

    Disable();

    if (m_vga) {
        m_vga->Release();
        m_vga = NULL;
    }

    if (m_engine) {
        if (this == m_engine->m_currentLayer) {
            m_engine->m_currentLayer = NULL;
        }

        for (Int32 i = 0; i < m_engine->m_elementCount; i++) {
            if (m_engine->m_collection[i] == this) {
                --m_engine->m_elementCount;
                assert(m_engine->m_elementCount >= 0);

                if ((m_engine->m_elementCount - i) > 0) {
                    memcpy(&m_engine->m_collection[i],
                        &m_engine->m_collection[i + 1],
                        m_engine->m_elementCount - i);
                }
            }
        }

        m_engine->Release();
    }

    GFX_EXPRESSION(DEBUG_DESTRUCTOR, kprintf("~CGrafixLayer() 0x%08x\n", this));

    g_lock.Unlock();
}

ECode CGrafixLayer::Enable(
    /* [in] */ const GrafixLayerFormat& layerFormat)
{
    ECode   ec = NOERROR;

    g_lock.Lock();

    if (m_enable) {
        goto Exit;
    }

    if (layerFormat.grafixLayerIndex != m_info.layer) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The layer is overlay%d\n", m_info.layer));
        ec = E_NOT_SUPPORTED;

        goto Exit;
    }

    if (!m_vga) {
        ec = DzFindService(L"device:display0", (IInterface **)&m_vga);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }

    switch (m_info.layer) {
        case 0: {   // baseplane
            assert(0 == layerFormat.grafixLayerIndex);

            MemoryBuf_<sizeof(FrameBufferFormat)> ezbOutFbFmt;
            MemoryBuf_<32> ezbModeName;
            MemoryBuf_<32> ezbOutAddr;
            FrameBufferFormat* pFbFmt;
            char szModeName[32] = "800x600x16";

            ezbModeName.Copy((Byte*)szModeName, strlen(szModeName));

            ec = m_vga->Control(3, ezbModeName, &ezbOutAddr, NULL);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

            // Get frame buffer format (width, height, color format ...)
            //
            ec = m_vga->Control(7, NULL_MEMORYBUF, &ezbOutFbFmt, NULL);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

            pFbFmt = (FrameBufferFormat *)ezbOutFbFmt.GetPayload();

            m_info.bitsPerPixel = pFbFmt->Bpp;

            switch (pFbFmt->Color) {
                case COLOR_RGB565:
                    m_info.pixelFormat = PixelFormat_RGB565;
                    break;
                    
                case COLOR_RGBT666:
                    m_info.pixelFormat = PixelFormat_RGBT666;
                    break;

                case COLOR_RGB24:
                    m_info.pixelFormat = PixelFormat_RGB24;
                    break;

                case COLOR_ARGB:
                    m_info.pixelFormat = PixelFormat_ARGB;
                    break;

                default:
                    GFX_EXPRESSION(ERRORLEVEL,
                        kprintf("Not support the format 0x%08x\n",
                        pFbFmt->Color));
                    ec = E_UNKNOWN_FORMAT;

                    goto Exit;
            }

            m_info.screen.x = 0;
            m_info.screen.y = 0;
            m_info.screen.width = pFbFmt->Width;
            m_info.screen.height = pFbFmt->Height;
            m_info.layer = 0;
            m_info.addressCount = 1;
            m_info.systemAddress[0] = *(Address*)ezbOutAddr.GetPayload();
            m_info.size[0] = pFbFmt->Width * pFbFmt->Height
                            * m_info.bitsPerPixel >> 3;

            m_engine->m_currentLayer = this;
            g_basePlaneLayer = this;

            GFX_EXPRESSION(INFORMATION,
                kprintf(
                "The screen width = %d, height = %d, byte per pixel = %d\n",
                m_info.screen.width, m_info.screen.height,
                m_info.bitsPerPixel));

            GFX_EXPRESSION(INFORMATION,
                kprintf("The hardware address is begin on 0x%08x\n",
                m_info.systemAddress[0]));

            break;
        }
        case 1: {   // overlay1
            assert(PixelFormat_RGBT666 == layerFormat.pixelFormat);
            assert(1 == layerFormat.overlayer);

            OverlayFormat* pOverlayFmt;
            MemoryBuf_<sizeof(OverlayFormat)> ezbInFbFmt;
            MemoryBuf_<sizeof(OverlayFormat)> ezbOutFbFmt;

            if (!RectangleValid((Rectangle *)&layerFormat.layerArea)) {
                GFX_EXPRESSION(ERRORLEVEL,
                kprintf("The input rectangle is invalid\n"));
                ec = E_INVALID_ARGUMENT;

                goto Exit;
            }

            memset(ezbInFbFmt.GetPayload(), 0, sizeof(OverlayFormat));

            pOverlayFmt = (OverlayFormat *)ezbInFbFmt.GetPayload();

            pOverlayFmt->Enable = TRUE;
            pOverlayFmt->Color = COLOR_RGBT666;
            pOverlayFmt->Layer = layerFormat.grafixLayerIndex;
            pOverlayFmt->Flag = layerFormat.overlayer;
            pOverlayFmt->Bpp = 32;
            pOverlayFmt->Width = layerFormat.layerArea.width;
            pOverlayFmt->Height = layerFormat.layerArea.height;
            pOverlayFmt->PosX = layerFormat.layerArea.x;
            pOverlayFmt->PosY = layerFormat.layerArea.y;
            ezbInFbFmt.SetUsed(sizeof(OverlayFormat));

            ec = m_vga->Control(8, ezbInFbFmt, &ezbOutFbFmt, NULL);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

            pOverlayFmt = (OverlayFormat *)ezbOutFbFmt.GetPayload();

            m_info.screen.x = pOverlayFmt->PosX;
            m_info.screen.y = pOverlayFmt->PosY;
            m_info.screen.width = pOverlayFmt->Width;
            m_info.screen.height = pOverlayFmt->Height;
            m_info.bitsPerPixel = pOverlayFmt->Bpp;
            m_info.layer = pOverlayFmt->Layer;
            m_info.pixelFormat = PixelFormat_RGBT666;
            m_info.addressCount = 1;
            m_info.systemAddress[0] = (UInt32)pOverlayFmt->pChannelFb[0];
            m_info.size[0] = pOverlayFmt->Width * pOverlayFmt->Height
                            * m_info.bitsPerPixel >> 3;

            assert(m_info.bitsPerPixel == 32);

            GFX_EXPRESSION(INFORMATION,
                kprintf(
                "The screen width = %d, height = %d, byte per pixel = %d\n",
                m_info.screen.width, m_info.screen.height,
                m_info.bitsPerPixel));

            GFX_EXPRESSION(INFORMATION,
                kprintf(
                "The hardware address is begin on 0x%08x, 0x%08x, 0x%08x\n",
                m_info.systemAddress[0]));

            m_engine->m_currentLayer = this;

            m_engine->m_stack->Update();

            break;
        }
        case 2: {   // overlay2
            assert(
                (PixelFormat_YUV422 == layerFormat.pixelFormat)
                || (PixelFormat_YUV420 == layerFormat.pixelFormat)
                || (PixelFormat_RGB565 == layerFormat.pixelFormat)
                || (PixelFormat_ARGB == layerFormat.pixelFormat));

            assert(0 == layerFormat.overlayer || 1 == layerFormat.overlayer);

            OverlayFormat* pOverlayFmt;
            MemoryBuf_<sizeof(OverlayFormat)> ezbInFbFmt;
            MemoryBuf_<sizeof(OverlayFormat)> ezbOutFbFmt;

            if (!RectangleValid((Rectangle *)&layerFormat.layerArea)) {
                GFX_ERROR_MESSAGE("The input rectangle is invalid\n");
                ec = E_INVALID_ARGUMENT;

                goto Exit;
            }

            memset(ezbInFbFmt.GetPayload(), 0, sizeof(OverlayFormat));

            pOverlayFmt = (OverlayFormat *)ezbInFbFmt.GetPayload();

            switch (layerFormat.pixelFormat) {
                case PixelFormat_YUV420:
                    pOverlayFmt->Color = COLOR_YCbCr420;
                    pOverlayFmt->Bpp = 16;
                    break;

                case PixelFormat_YUV422:
                    pOverlayFmt->Color = COLOR_YCbCr422;
                    pOverlayFmt->Bpp = 16;
                    break;

                case PixelFormat_RGB565:
                    pOverlayFmt->Color = COLOR_RGB565;
                    pOverlayFmt->Bpp = 16;
                    break;
#ifdef _neptune
                case PixelFormat_ARGB:
                    pOverlayFmt->Color = COLOR_ARGB;
                    pOverlayFmt->Bpp = 32;
                    break;
#endif // _neptune
                default:
                    GFX_EXPRESSION(ERRORLEVEL,
                        kprintf("The pixelformat 0x%08x not support\n",
                        layerFormat.pixelFormat));
                    ec = E_NOT_SUPPORTED;

                    goto Exit;
            }

            pOverlayFmt->Enable = TRUE;
            pOverlayFmt->Layer = layerFormat.grafixLayerIndex;
            pOverlayFmt->Flag = layerFormat.overlayer;
            pOverlayFmt->Width = layerFormat.layerArea.width;
            pOverlayFmt->Height = layerFormat.layerArea.height;
            pOverlayFmt->PosX = layerFormat.layerArea.x;
            pOverlayFmt->PosY = layerFormat.layerArea.y;
            ezbInFbFmt.SetUsed(sizeof(OverlayFormat));

            ec = m_vga->Control(8, ezbInFbFmt, &ezbOutFbFmt, NULL);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

            pOverlayFmt = (OverlayFormat *)ezbOutFbFmt.GetPayload();

            switch (pOverlayFmt->Color) {
                case COLOR_YCbCr420:
                    m_info.pixelFormat = PixelFormat_YUV420;
                    break;

                case COLOR_YCbCr422:
                    m_info.pixelFormat = PixelFormat_YUV422;
                    break;

                case COLOR_RGB565:
                    m_info.pixelFormat = PixelFormat_RGB565;
                    break;
#ifdef _neptune
                case COLOR_ARGB:
                    m_info.pixelFormat = PixelFormat_ARGB;
                    break;
#endif // _neptune
                default:
                    GFX_EXPRESSION(ERRORLEVEL,
                        kprintf("The pixelformat 0x%08x not support\n",
                        layerFormat.pixelFormat));
                    ec = E_NOT_SUPPORTED;

                    goto Exit;
            }

            m_info.screen.x = pOverlayFmt->PosX;
            m_info.screen.y = pOverlayFmt->PosY;
            m_info.screen.width = pOverlayFmt->Width;
            m_info.screen.height = pOverlayFmt->Height;
            m_info.bitsPerPixel = pOverlayFmt->Bpp;
            m_info.layer = pOverlayFmt->Layer;
            m_info.addressCount = 3;
            m_info.systemAddress[0] = (UInt32)pOverlayFmt->pChannelFb[0];
            m_info.size[0] =  pOverlayFmt->ChannelSize[0];
            m_info.systemAddress[1] = (UInt32)pOverlayFmt->pChannelFb[1];
            m_info.size[1] =  pOverlayFmt->ChannelSize[1];
            m_info.systemAddress[2] = (UInt32)pOverlayFmt->pChannelFb[2];
            m_info.size[2] =  pOverlayFmt->ChannelSize[2];

#if !(defined(_arm) && defined (_neptune))
#if !(defined (_C28))
            g_basePlaneLayer->m_info.pixelFormat = PixelFormat_RGBT555;

            GFX_EXPRESSION(INFORMATION,
                kprintf(
                "The screen width = %d, height = %d, byte per pixel = %d\n",
                m_info.screen.width, m_info.screen.height,
                m_info.bitsPerPixel));

            GFX_EXPRESSION(INFORMATION,
                kprintf(
                "The hardware address is begin on 0x%08x, 0x%08x, 0x%08x\n",
                m_info.systemAddress[0], m_info.systemAddress[1],
                m_info.systemAddress[2]));
#endif
#endif
            m_engine->m_stack->Update();

            break;
        }
        default: {
            GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The pixel format 0x%08x does not support\n",
            layerFormat.pixelFormat));
            ec = E_NOT_SUPPORTED;

            goto Exit;
        }
    }

    m_enable = TRUE;

    ec = m_engine->EnableStack(m_info.screen.width, m_info.screen.height);
    if (GFX_FAILED(ec)) {
        m_enable = FALSE;
        goto Exit;
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixLayer::Disable()
{
    ECode   ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The layer does not be enable\n"));
        ec = E_LAYER_NOT_ENABLE;

        goto Exit;
    }

    assert(m_vga);

    if (2 == m_info.layer) {
        MemoryBuf_<sizeof(FrameBufferFormat)> ezbOutFbFmt;
        MemoryBuf_<4> ebbInBuf;
        *(UInt16 *)ebbInBuf.GetPayload() = 2;
        ebbInBuf.SetUsed(2);

        ec = m_vga->Control(10, ebbInBuf, &ezbOutFbFmt, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }

#ifndef _neptune
#if !(defined (_C28))
        g_basePlaneLayer->m_info.pixelFormat = PixelFormat_RGB565;
#endif
#endif
        m_engine->m_stack->Update();
    }
    else if (1 == m_info.layer) {
        MemoryBuf_<sizeof(FrameBufferFormat)> ezbOutFbFmt;
        MemoryBuf_<4> ebbInBuf;

        *(UInt16 *)ebbInBuf.GetPayload() = 1;
        ebbInBuf.SetUsed(2);

        ec = m_vga->Control(10, ebbInBuf, &ezbOutFbFmt, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }

        m_engine->m_currentLayer = g_basePlaneLayer;
    }
    else {
#ifdef _x86
        ec = m_vga->Control(6, NULL_MEMORYBUF, NULL, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
#endif
    }

    m_enable = FALSE;

    ec = m_engine->DisableStack();
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    if (m_engine->m_stack) {
        m_engine->m_stack->Update();
    }

Exit:
    g_lock.Unlock();

    return ec;
}
